package com.gallop.mq.core.template.handler.registry;

import com.gallop.mq.core.exception.MQException;
import com.gallop.mq.core.template.handler.DeliveryContext;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * MQ消息监听创建逻辑的动态代理实现，自动实现MQ监听回调中的消息分发。以RabbitMQ为例，使用此代理创建的监听等同于：
 *
 * <pre>
 *      handlerRegistry.setPreprocessor(RegistryPreprocessor.<com.rabbitmq.client.Consumer, byte[]>builder()
 *                  // 注册自定义的监听creator，通过代码触发handleDelivery，实现消息分发
 *                 .listenerCreator(context -> new DefaultConsumer(channel) {
 *                     @Override
 *                     public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
 *                          // 手动转发
 *                          handlerRegistry.handleDelivery(context, consumerTag, envelope, properties, body);
 *                     }
 *                 })
 *                 // ...
 *                 .build()
 * </pre>
 *
 *
 * <p>
 * 设计目的： 为具体的{@link com.gallop.mq.MQTemplate}实现类，提供快速统一的消息监听注册逻辑。
 * 消息监听将统一由{@link MessageDeliveryHandlerRegistry}处理。
 * <p>
 * 工作原理：当MQ监听消息到达时，默认调用{@link MessageDeliveryHandlerRegistry#handleDelivery(DeliveryContext, Object...)}，
 * 将收到的消息统一分发处理；以统一不同MQ监听的注册操作，增强组件的监听逻辑内聚。
 * <p>
 * 不选用此实现时的写法：
 * <p>
 * 当具体{@link com.gallop.mq.MQTemplate}的消息监听器需要注册时，
 * 在{@link MessageDeliveryHandlerRegistry#setPreprocessor(RegistryPreprocessor)}的builder中
 * 注册自定义的{@link ListenerCreator}实现，手动调用{@link MessageDeliveryHandlerRegistry#handleDelivery(DeliveryContext, Object...)}方法。
 * <p>
 * 例：
 *
 * <pre>
 *
 *      // 在rabbitMQ实现类的 RegistryHolder#initRegistry方法中：
 *      handlerRegistry.setPreprocessor(RegistryPreprocessor.<com.rabbitmq.client.Consumer, byte[]>builder()
 *                  // 注册自定义的监听creator，通过代码触发handleDelivery，实现消息分发
 *                 .listenerCreator(context -> new DefaultConsumer(channel) {
 *                     @Override
 *                     public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
 *                         handlerRegistry.handleDelivery(context, consumerTag, envelope, properties, body);
 *                     }
 *                 })
 *                 // ...
 *                 .build()
 * </pre>
 *
 * @param <T> 具体MQ的监听器类型
 */
@Slf4j
public class DynamicProxyListenerCreator<T> implements ListenerCreator<T> {

    private final MessageDeliveryHandlerRegistry handlerRegistry;
    private final CreatorParam creatorParam;

    /**
     * 构造一个MQ监听的动态代理，代理会默认接口中的第一个方法为消息接收方法并加以拦截。
     * <p>
     * 收到消息后转发{@link MessageDeliveryHandlerRegistry#handleDelivery(DeliveryContext, Object...)}
     *
     * @param handlerRegistry 消息处理逻辑
     * @param type            MQ监听接口类型
     * @param <T>             MQ监听接口类型
     * @return 使用动态代理实现了MQ监听接口的实例
     */
    public static <T> ListenerCreator<T> ofInterface(MessageDeliveryHandlerRegistry handlerRegistry,
                                                     Class<T> type) {
        return ofInterface(handlerRegistry, type, type.getMethods()[0]);
    }

    /**
     * 构造一个MQ监听的动态代理，代理会对指定的接口方法做逻辑拦截。
     * <p>
     * 收到消息后转发{@link MessageDeliveryHandlerRegistry#handleDelivery(DeliveryContext, Object...)}
     *
     * @param handlerRegistry 消息处理逻辑
     * @param type            MQ监听接口类型
     * @param onMessageMethod MQ监听接口消息接收方法
     * @param <T>             MQ监听接口类型
     * @return 使用动态代理实现了MQ监听接口的实例
     */
    public static <T> ListenerCreator<T> ofInterface(MessageDeliveryHandlerRegistry handlerRegistry,
                                                     Class<T> type,
                                                     Method onMessageMethod) {
        if (type == null || !type.isInterface()) {
            throw new MQException("Listener type " + type + " is not an interface");
        }
        return new DynamicProxyListenerCreator<>(handlerRegistry, InterfaceCreatorParam.builder()
                .onMessageMethod(onMessageMethod)
                .type(type)
                .build());
    }

    /**
     * 构造一个MQ监听的动态代理，代理会默认接口中的第一个方法为消息接收方法并加以拦截。
     * <p>
     * 收到消息后转发{@link MessageDeliveryHandlerRegistry#handleDelivery(DeliveryContext, Object...)}
     *
     * @param handlerRegistry 消息处理逻辑
     * @param type            MQ监听接口类型
     * @param <T>             MQ监听接口类型
     * @return 使用动态代理实现了MQ监听接口的实例
     */
    public static <T> ListenerCreator<T> ofGenericInterface(MessageDeliveryHandlerRegistry handlerRegistry,
                                                            Class<?> type) {
        return (ListenerCreator<T>) ofInterface(handlerRegistry, type);
    }

    /**
     * 构造一个MQ监听的动态代理，代理会对指定的接口方法做逻辑拦截。
     * <p>
     * 收到消息后转发{@link MessageDeliveryHandlerRegistry#handleDelivery(DeliveryContext, Object...)}
     *
     * @param handlerRegistry 消息处理逻辑
     * @param type            MQ监听接口类型
     * @param onMessageMethod MQ监听接口消息接收方法
     * @param <T>             MQ监听接口类型
     * @return 使用动态代理实现了MQ监听接口的实例
     */
    public static <T> ListenerCreator<T> ofGenericInterface(MessageDeliveryHandlerRegistry handlerRegistry,
                                                            Class<?> type,
                                                            Method onMessageMethod) {
        return (ListenerCreator<T>) ofInterface(handlerRegistry, type, onMessageMethod);
    }

    public static <T> ListenerCreator<T> ofObject(MessageDeliveryHandlerRegistry handlerRegistry,
                                                  T instance,
                                                  Method onMessageMethod) {
        return ofObject(handlerRegistry, instance, onMessageMethod, null);
    }

    public static <T> ListenerCreator<T> ofObject(MessageDeliveryHandlerRegistry handlerRegistry,
                                                  T instance,
                                                  Method onMessageMethod,
                                                  Constructor<? extends T> constructor,
                                                  Object... constructorParams) {
        return new DynamicProxyListenerCreator<>(handlerRegistry, ObjectCreatorParam.builder()
                .onMessageMethod(onMessageMethod)
                .instance(instance)
                .constructor(constructor)
                .constructParams(constructorParams)
                .build());
    }

    private DynamicProxyListenerCreator(MessageDeliveryHandlerRegistry handlerRegistry, CreatorParam creatorParam) {
        this.handlerRegistry = handlerRegistry;
        this.creatorParam = creatorParam;
    }

    @Override
    public T create(DeliveryContext deliveryContext) {
        return creatorParam.createProxy(handlerRegistry, deliveryContext);
    }

    /**
     * MQ监听器代理创建接口
     */
    private interface CreatorParam {
        <T> T createProxy(MessageDeliveryHandlerRegistry handlerRegistry, DeliveryContext deliveryContext);
    }

    @Data
    @Builder
    private static class ObjectCreatorParam implements CreatorParam {

        private Object instance;
        private Method onMessageMethod;
        private Constructor<?> constructor;
        private Object[] constructParams;

        @Override
        public <T> T createProxy(MessageDeliveryHandlerRegistry handlerRegistry, DeliveryContext deliveryContext) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(instance.getClass());
            enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
                if (method.equals(onMessageMethod)) {
                    handlerRegistry.handleDelivery(deliveryContext, objects);
                    return null;
                }
                return method.invoke(o, objects);
            });
            if (constructor == null || constructParams == null || constructParams.length <= 0) {
                return (T) enhancer.create();
            }
            return (T) enhancer.create(constructor.getParameterTypes(), constructParams);
        }
    }

    @Data
    @Builder
    private static class InterfaceCreatorParam implements CreatorParam {
        private Class<?> type;
        private Method onMessageMethod;

        @Override
        public <T> T createProxy(MessageDeliveryHandlerRegistry handlerRegistry, DeliveryContext deliveryContext) {
            T proxy = (T) Proxy.newProxyInstance(this.type.getClassLoader(), new Class[]{type}, new InvocationHandler() {
                @Override
                public Object invoke(Object o, Method method, Object[] objects) throws InvocationTargetException, IllegalAccessException {
                    if (method.equals(onMessageMethod)) {
                        handlerRegistry.handleDelivery(deliveryContext, objects);
                        return null;
                    }
                    throw new MQException("unhandled method in proxy. method=" + method);
                }
            });
            return proxy;
        }
    }
}
